package logger

import (
	"context"
	"fmt"
	"io"
	"log"
	"runtime"
	"time"
)

type Level int8

type Fields map[string]interface{}

const (
	LevelDebug Level = iota
	LevelInfo
	LevelWarn
	LevelError
	LevelFatal
	LevelPanic
)

type Logger struct {
	newLogger *log.Logger
	ctx       context.Context
	fields    Fields
	caller    []string
}

func NewLogger(w io.Writer, prefix string, flag int) *Logger {
	l := log.New(w, prefix, flag)
	return &Logger{
		newLogger: l,
	}
}

func (l *Logger) clone() *Logger {
	ll := *l
	return &ll
}

func (l *Logger) WithFields(f Fields) *Logger {
	ll := l.clone()
	if ll.fields == nil {
		ll.fields = make(Fields)
	}
	for k, v := range f {
		ll.fields[k] = v
	}
	return ll
}

func (l *Logger) WithCtx(ctx context.Context) *Logger {
	ll := l.clone()
	ll.ctx = ctx
	return ll
}

func (l *Logger) WithCaller(skip int) *Logger {
	ll := l.clone()
	pc, file, line, ok := runtime.Caller(skip)
	if ok {
		f := runtime.FuncForPC(pc)
		ll.caller = []string{fmt.Sprintf("%s %d %s", file, line, f.Name())}
	}
	return ll
}

func (l *Logger) JSONFormat(level Level, msg string) map[string]interface{} {
	data := make(Fields, len(l.fields)+4)
	data["level"] = level
	data["time"] = time.Now().Local().UnixNano()
	data["msg"] = msg
	data["caller"] = l.caller
	if len(l.fields) > 0 {
		for k, v := range l.fields {
			if _, ok := data[k]; ok {
				data[k] = v
			}
		}
	}
	return data
}

func (l Level) String() string {
	switch l {
	case LevelDebug:
		return "debug"
	}
	return ""
}
